package com.anji.hyperneat.modular;

import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;

import org.jgap.Chromosome;

import com.anji.tug.Objective;
import com.anji.tug.TugManager;
import com.anji.util.Properties;


/**
 * Modular HyperNEAT Fitness Function for Targeting Unachieved Goals
 * @author slusk
 *
 */
public abstract class ModularHnTugFf extends ModularHyperNeatFitnessFunction {

	private static final long serialVersionUID = -1109068829840246903L;

	protected ConcurrentHashMap<Chromosome, HashMap<String, Float>> individualMetrics;
//	protected HashMap<String, Objective> objectives;
	protected TugManager tugManager;
	
	
	@Override
	public void init(Properties props) {
		super.init(props);
		int popSize = props.getIntProperty("popul.size");
		individualMetrics = new ConcurrentHashMap<Chromosome, HashMap<String, Float>>(popSize, 0.75f, numThreads);
		
	}
	
	/**
	 * Extensions to this class must initialize objectives, preferably during the init() call.
	 * Create Objectives manually, or load from XML via ObjectivesLoader.
	 */
	public abstract void initializeTugManager(Properties props);

	
    /**
     * Evaluate each chromosome in genotypes.
     *
     * @param genotypes <code>List</code> contains <code>Chromosome</code> objects.
     */
    public void evaluate(List<Chromosome> genotypes) {
    	initialiseEvaluation();
    	
    	bestPerformance = targetPerformanceType == 1 ? 0 : Float.MAX_VALUE;
    	
        chromosomesIterator = genotypes.iterator();
        evaluatorsFinishedCount = 0;
        for (Evaluator ev : evaluators)
            ev.go();
        
        while(true) {
            try {
                synchronized(this) {
                    if (evaluatorsFinishedCount == evaluators.length)
                        break;
                    wait();
                }
            } catch (InterruptedException ignore) {System.out.println(ignore);}
        }
        
        lastBestChrom = newBestChrom;
        lastBestPerformance = bestPerformance;
        
        /*
         * Targeting Unachieved goals.
         * Evaluation of each chromosome complete, the individual metrics hash should now have been populated.
         */
        tugManager.updateObjectives(individualMetrics);
        
        // Assign fitness to each chromosome, based on the current objectives, as controlled by the tugmanager
        for (Chromosome chromosome : genotypes) {
        	calcAndAssignFitness( chromosome );
        }
        
        // reset individual metrics, only need to store one gen at a time.
        individualMetrics.clear();
        
        endRun = false;
        
        // TODO implement me
        //if we should scale the substrate
//        if (scaleCount < scaleTimes && scaleFactor > 1 && 
//        		((targetPerformanceType == 1 && bestPerformance >= scalePerformance) || (targetPerformanceType == 0 && bestPerformance <= scalePerformance))
//        	) {
//        	//allow sub-class to make necessary changes
//        	scale(scaleCount, scaleFactor);
//        	for (Evaluator ev : evaluators)
//        		ev.resetSubstrate(); //don't reuse old size substrate
//        	transcriber.resize(width, height, connectionRange);
//        	        	
//        	scaleCount++;
//        }
        
        if ((targetPerformanceType == 1 && bestPerformance >= targetPerformance) || (targetPerformanceType == 0 && bestPerformance <= targetPerformance)) {
        	System.out.println("End run, solution found. bestPerformance: " + bestPerformance + ", targetPerformance: " + targetPerformance);
        	endRun = true;
        }
        
        generation++;
    }

	protected void calcAndAssignFitness(Chromosome chromosome) {
		float fitness = 0;
		int numGoalsOn = 0;
		
		
		for (Entry<String, Objective> entry : tugManager.getObjectivesSet()) {
			Objective objective = entry.getValue();
			
			if (objective.isOn) {
				fitness += (Math.min(individualMetrics.get(chromosome).get(entry.getKey()), objective.finalGoal) / objective.finalGoal) / tugManager.getNumObjectivesOn();
				numGoalsOn++;
			}
		}
		
//		fitness /= numGoalsOn;
		
		chromosome.setFitnessValue((int) (fitness * getMaxFitnessValue()));
	}




}

